package com.xunk.system.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.xunk.common.core.constant.CacheConstants;
import com.xunk.common.core.constant.Constants;
import com.xunk.common.core.constant.HttpStatus;
import com.xunk.common.core.constant.MessageNotifyConstants;
import com.xunk.common.core.domain.UploadFile;
import com.xunk.common.core.exception.CustomException;
import com.xunk.common.core.utils.*;
import com.xunk.common.redis.service.RedisService;
import com.xunk.common.security.utils.SecurityUtils;
import com.xunk.equipment.api.RemoteEquipmentService;
import com.xunk.equipment.api.domain.MessageNotificationDO;
import com.xunk.system.api.domain.SysUser;
import com.xunk.system.domain.*;
import com.xunk.system.mapper.SysHomeMapper;
import com.xunk.system.mapper.SysTenementMapper;
import com.xunk.system.mapper.SysVisitorLogMapper;
import com.xunk.system.service.ISysTenementService;
import com.xunk.system.service.UploadFileService;
import com.xunk.system.vo.BillSummaryDataVO;
import com.xunk.system.vo.HomeBillInfo;
import com.xunk.system.vo.VisitLogReqParamVO;
import com.xunk.system.vo.hsh.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 社区物业Service业务层处理
 * 
 * @author ruoyi
 * @date 2021-03-18
 */
@Slf4j
@Service
public class SysTenementServiceImpl implements ISysTenementService
{
    @Autowired
    private SysTenementMapper sysTenementMapper;

    @Autowired
    private SysVisitorLogMapper sysVisitorLogMapper;

    @Autowired
    private RedisService service;

    @Value("${tenement.hsh.platformId}")
    private String platformId;

    @Value("${tenement.hsh.sign}")
    private String sign;

    @Value("${tenement.hsh.domain}")
    private String domain;

    private static final String TENE_BILL_ELE_TYPE = "电梯运行费";

    private static final String TENE_BILL_HOME_TYPE = "物业管理费-住宅";

    @Autowired
    private UploadFileService uploadFileService;

    @Autowired
    private RemoteEquipmentService remoteEquipmentService;

    @Autowired
    private SysHomeMapper homeMapper;

    /**
     * 查询社区物业
     * 
     * @param id 社区物业ID
     * @return 社区物业
     */
    @Override
    public SysTenement selectSysTenementById(Long id)
    {
        return sysTenementMapper.selectSysTenementById(id);
    }

    /**
     * 查询社区物业列表
     * 
     * @param sysTenement 社区物业
     * @return 社区物业
     */
    @Override
    public List<SysTenementDetail> selectSysTenementList(SysTenementDetail sysTenement)
    {
        return sysTenementMapper.selectSysTenementList(sysTenement);
    }

    /**
     * 新增社区物业
     * 
     * @param sysTenement 社区物业
     * @return 结果
     */
    @Override
    public int insertSysTenement(SysTenement sysTenement)
    {
        //  校验所选部门是否已存在管理的物业
        check(sysTenement.getDeptId());
        sysTenement.setDistrictCode(sysTenementMapper.getDistrictCode(sysTenement));
        return sysTenementMapper.insertSysTenement(sysTenement);
    }

    /**
     * 修改社区物业
     * 
     * @param sysTenement 社区物业
     * @return 结果
     */
    @Override
    public int updateSysTenement(SysTenement sysTenement)
    {
        check(sysTenement.getDeptId());
        sysTenement.setDistrictCode(sysTenementMapper.getDistrictCode(sysTenement));
        return sysTenementMapper.updateSysTenement(sysTenement);
    }

    /**
     * 批量删除社区物业
     * 
     * @param ids 需要删除的社区物业ID
     * @return 结果
     */
    @Override
    public int deleteSysTenementByIds(Long[] ids)
    {
        return sysTenementMapper.deleteSysTenementByIds(ids);
    }

    /**
     * 删除社区物业信息
     * 
     * @param id 社区物业ID
     * @return 结果
     */
    @Override
    public int deleteSysTenementById(Long id)
    {
        return sysTenementMapper.deleteSysTenementById(id);
    }

    @Override
    public void loginTenement(Long homeId) {

        //  根据homeId来获取对应的唯一房主手机号
        String phone = sysTenementMapper.selectPhoneNum(homeId);
        XunkAssert.notEmpty(phone,"物业系统登录失败，未找到房主手机信息");
        //  根据deptId来区分物业
        SysTenementModel model = sysTenementMapper.selectTenementInfo(homeId);
        XunkAssert.notEmpty(model,"小区归属物业未定义");
        XunkAssert.notEmpty(model.getName(),"小区归属物业未定义");
        switch (model.getName()){
            case "海尚海物业":
                if (StringUtils.isEmpty((String)service.getCacheObject(CacheConstants.TENEMENT_LOGIN_TOKEN + homeId))){

                    //  登录接口：得到返回的token和物业方面对家庭的定义id  统统存入redis
                    HashMap<String,String> map = new HashMap<>();
                    map.put("phone","+86" + phone);
                    map.put("platformId",platformId);
                    map.put("sign", MD5Utils.MD5Lower("+86" + phone + sign));
                    Map<String, String> header = new HashMap<>();
                    header.put("Content-Type","application/json");
                    JSONObject object = HttpUtil.doPoststr(domain+"/estate-frontend/platform/login",
                            JSONObject.toJSONString(map),header);
                    HaiShangHaiResVO<HshLoginResponseVO> resVO = object.toJavaObject(new TypeReference<HaiShangHaiResVO<HshLoginResponseVO>>(){});

                    doCheckResponse(resVO,"物业系统登录失败");
                    String token = resVO.getToken();
                    header.put("X-HainaCloud-Token",token);
                    //  获取所有社区信息
                    JSONObject getstr = HttpUtil.doGetstr(domain + "/estate-frontend/estates", header);

                    HaiShangHaiListResVO<HshCityEstatesVO> listResVO = getstr.toJavaObject(new TypeReference<HaiShangHaiListResVO<HshCityEstatesVO>>(){});
                    doCheckResponse(listResVO,"找不到房屋对应的社区信息");
                    String estateId = "";
                    for (HshCityEstatesVO estates : listResVO.getBody()) {
                        List<HshCityEstates> estatesList = estates.getCityEstates();
                        for (HshCityEstates cityEstates : estatesList) {
                            //  所在同一个城市
                            if (cityEstates.getCityName().equals(model.getCity())){
                                List<HshEstates> hshEstates = cityEstates.getResidentialEstates();
                                hshEstates.addAll(CollectionUtils.isEmpty(cityEstates.getBusinessEstates()) ?
                                            new ArrayList<>() : cityEstates.getBusinessEstates());
                                for (HshEstates hshEstate : hshEstates) {
                                    //  小区名称一致
                                    if (hshEstate.getName().equals(model.getDeptName())) {
                                        estateId = hshEstate.getId();
                                    }
                                }
                            }
                        }
                    }
                    XunkAssert.notEmpty(estateId,"sorry，找不到您对应的社区信息");
                    //  拿到社区id，来获得改社区下的房屋信息   TODO 目前只考虑一个社区一套房子的情况，后期可扩展
                    getstr = HttpUtil.doGetstr(domain + "/estate-frontend/families?residentialEstateId="+estateId, header);
                    HaiShangHaiListResVO<HshHouseVO> house = getstr.toJavaObject(new TypeReference<HaiShangHaiListResVO<HshHouseVO>>(){});
                    doCheckResponse(house,"房屋信息查询失败");
                    HshHouseVO switchData = new HshHouseVO();
                    XunkAssert.isTrue(house.getBody().size() != 0,"未找到您对应的房屋信息");
                    for (HshHouseVO hshHouseVO : house.getBody()) {
                        switchData = hshHouseVO;
                    }
                    Map<String,String> workSpace = new HashMap<>();
                    workSpace.put("familyId",switchData.getFamilyId());
                    workSpace.put("houseId",switchData.getHouseId());
                    workSpace.put("residentialEstateId",estateId);
                    //  选定这个家庭，定向切换工作空间
                    JSONObject poststr = HttpUtil.doPoststr(domain + "/estate-frontend/session/workspace", JSONObject.toJSONString(workSpace), header);
                    HaiShangHaiResVO<HshLoginResponseVO> swRes = poststr.toJavaObject(new TypeReference<HaiShangHaiResVO<HshLoginResponseVO>>(){});
                    doCheckResponse(swRes,"选择家庭失败");
                    //  将海尚海物业对应该房子的id定义 与我们系统的房屋定义对应关系存入resis
                    service.setCacheObject(CacheConstants.TENEMENT_FAMILY + homeId,switchData.getHouseId());
                    //  后续交互的token
                    service.setCacheObject(CacheConstants.TENEMENT_LOGIN_TOKEN + homeId,swRes.getToken(),30, TimeUnit.DAYS);
                }
                break;
            default:
        }
    }

    @Override
    public List<HshMatterTypeResVO> getMatterType() {

        JSONObject object = HttpUtil.doPoststr("http://api.test.haikehui.net/estate-frontend/repair/options", "");
        HaiShangHaiResVO<?> resVO = object.toJavaObject(new TypeReference<HaiShangHaiResVO<?>>(){});
        return JSONObject.parseArray(resVO.getBody().toString(), HshMatterTypeResVO.class);
    }

    @Override
    public void doSubmitMatter(HshMatterSubmitVO vo) {

        JSONObject object = HttpUtil.doPoststr(domain + "/estate-frontend/repair/reports", JSONObject.toJSONString(vo), getTenementHeader(vo.getHomeId()));
        HaiShangHaiResVO<?> resVO = object.toJavaObject(HaiShangHaiResVO.class);
        doCheckResponse(resVO,"上报失败");
        // 入库
        SysMatterLog matterLog = new SysMatterLog();
        matterLog.setMatterType(vo.getType() + "");
        matterLog.setMatterAddress(vo.getLocation());
        matterLog.setMatterContent(vo.getReportDesc());
        matterLog.setSubmitName(vo.getReportName());
        matterLog.setSubmitPhone(vo.getReportMobile());
        matterLog.setSendId("1");
        matterLog.setSubmitId(SecurityUtils.getUserId() + "");
        sysTenementMapper.insertMatterLog(matterLog);

        //  消息通知！
        List<MessageNotificationDO> messageList = new ArrayList<>();
        List<SysUser> sysUsers = homeMapper.selectHomeMemberByHomeId(vo.getHomeId());
        sysUsers.parallelStream().forEach(sysUser -> {

            MessageNotificationDO message = new MessageNotificationDO();
            message.setContent("您有一条报事报修信息已提交，正在处理中，请您耐心等待！");
            message.setType(MessageNotifyConstants.SYSTEM_TYPE);
            message.setTitle("报事报修通知");
            message.setUserId(sysUser.getUserId());
            message.setDeptId(sysUser.getDeptId());
            message.setCreateTime(new Date());
            messageList.add(message);
        });
        remoteEquipmentService.insertMessageNotification(messageList);
    }

    @Override
    public HshMatterDetailVO getMatterDetail(Long homeId, String id) {
        JSONObject object = HttpUtil.doGetstr(domain + "/estate-frontend/repair/reports/"+id, getTenementHeader(homeId));
        HaiShangHaiResVO<HshMatterDetailVO> resVO = object.toJavaObject(new TypeReference<HaiShangHaiResVO<HshMatterDetailVO>>(){});
        doCheckResponse(resVO,"获取保修详情失败");
        return resVO.getBody();
    }


    @Override
    public HshPageVo<HshNoticeVo> getTenementNotice(Long homeId, Integer current, Integer size) {

        JSONObject object = HttpUtil.doGetstr(domain + "/estate-frontend/notice/notices?current="+current+"&fromType="+1+"&size="+size,
                getTenementHeader(homeId));
        HaiShangHaiResVO<HshPageVo<HshNoticeVo>> swRes = object.toJavaObject(new TypeReference<HaiShangHaiResVO<HshPageVo<HshNoticeVo>>>(){});
        doCheckResponse(swRes,"获取通知公告失败");
        HshPageVo<HshNoticeVo> body = swRes.getBody();
        body.getRecords().add(new HshNoticeVo("公司各部门：\n" +
                "\n" +
                "\n" +
                "\n" +
                "　　值五一劳动节来临之际，根据国务院办公厅公布的《2021年节假日安排的通知》的有关规定，结合我公司实际情况，经领导班子研究决定，现将2021年五一劳动节放假事项通知如下：\n" +
                "\n" +
                "\n" +
                "\n" +
                "　　一、五一劳动节放假时间定为5月1日，与周末连休。\n" +
                "\n" +
                "　　二、各部门接通知后，妥善安排好值班工作，并将各部门值班表于2021年5月4日下午1:30以前报公司办公室。\n" +
                "\n" +
                "　　三、各部门要加强对值班人员的管理，认真落实公司突发事件预案制度，切实做好公司防火、安全、保卫等工作，发现苗头要及时向公司办公室值班人员报告。\n" +
                "\n" +
                "　　\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t公司办公室值班电话：13122272095\n" +
                "\n" +
                "　　\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t砖头有限公司\n" +
                "\n" +
                "\n" +
                "\n" +
                "　　\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t2021年4月27日 ",
                DateUtils.datePath(),"五一放假通知"));
        return swRes.getBody();
    }

    @Override
    public String createVisitorCode(HshVisitorDetailVO detailVO) {

        Long homeId = detailVO.getHomeId();
        Map<String, String> header = getTenementHeader(homeId);
        Date date;
        try {
            //  2021/06/09 11:07
            date = DateUtils.parseDate(detailVO.getTime(), "yyyy/MM/dd HH:mm");
        } catch (ParseException e) {
            e.printStackTrace();
            throw new CustomException("预约时间格式不正确");
        }
        XunkAssert.isTrue(new Date().getTime() < date.getTime(),"预约时间不可小于当前时间");
        detailVO.setOrderTime(Instant.ofEpochMilli(date.getTime()));
        JSONObject object = HttpUtil.doPutstr(domain + "/estate-frontend/visitor", JSONObject.toJSONString(detailVO),header);
        HaiShangHaiResVO<String> resVO = object.toJavaObject(new TypeReference<HaiShangHaiResVO<String>>(){});
        System.out.println(JSONObject.toJSONString(resVO));
        doCheckResponse(resVO,"生成失败");
        SysVisitorLog visitLog = new SysVisitorLog();
        visitLog.setHomeId(homeId);
        visitLog.setVisitorName(detailVO.getVisitorName());
        visitLog.setVisitorDate(date);
        visitLog.setVisitorSex(detailVO.getVisitorGender());
        visitLog.setIsDrive(detailVO.getIsDriver() + "");
        visitLog.setIsSend("0");
        visitLog.setPassQrCode(resVO.getBody());
        visitLog.setCarNum(detailVO.getVisitorCarNum());
        visitLog.setCreateTime(new Date());
        sysVisitorLogMapper.insertSysVisitorLog(visitLog);
        return resVO.getBody();
    }

    @Override
    public List<HshHouseInfoVO> getHouses(Long homeId) {

        JSONObject object = HttpUtil.doGetstr(domain + "/estate-frontend/fees/houses", getTenementHeader(homeId));
        HaiShangHaiListResVO<HshHouseInfoVO> listResVO = object.toJavaObject(new TypeReference<HaiShangHaiListResVO<HshHouseInfoVO>>(){});
        doCheckResponse(listResVO,"获取访问地址列表失败");
        return listResVO.getBody();
    }

    @Override
    public BillSummaryDataVO getFeeBill(Long homeId, Integer pageNum) {

        String id = service.getCacheObject(CacheConstants.TENEMENT_FAMILY + homeId);
        //  冒昧的接海客会的生产环境
        JSONObject object = HttpUtil.doGetstr("http://api.haikehui.net" + "/estate-frontend/fees/fees/interior?houseId=" + id, getTenementHeader(homeId));
        HaiShangHaiListResVO<HshBillFeeInfoVO> listResVO = object.toJavaObject(new TypeReference<HaiShangHaiListResVO<HshBillFeeInfoVO>>(){});
        doCheckResponse(listResVO,"查询账单失败");

        AtomicReference<String> ownerName = new AtomicReference<>("");
        List<HshBillFeeInfoVO> bills = listResVO.getBody();
        Map<String,Map<String, BigDecimal>> map = new HashMap<>();
        Map<String, BigDecimal> unpaidMap = new HashMap<>();
        bills.forEach(bill -> {

            ownerName.set(bill.getOwnerName());
            if (bill.getYsamount() > 0){
                Map<String, BigDecimal> detailMap = map.get(bill.getDshpay());
                if(detailMap == null){
                    detailMap = new HashMap<>();
                }
                detailMap.put(bill.getItemname(), BigDecimal.valueOf(bill.getYsamount()));
                map.put(bill.getDshpay(),detailMap);
            }else {
                if (TENE_BILL_ELE_TYPE.equals(bill.getItemname())){
                    BigDecimal eleBill = unpaidMap.get(TENE_BILL_ELE_TYPE) == null ? new BigDecimal(0) : unpaidMap.get(TENE_BILL_ELE_TYPE);
                    unpaidMap.put(TENE_BILL_ELE_TYPE,eleBill.add(BigDecimal.valueOf(bill.getYsamount())));
                }else {
                    BigDecimal homeBill = unpaidMap.get(TENE_BILL_HOME_TYPE) == null ? new BigDecimal(0) : unpaidMap.get(TENE_BILL_HOME_TYPE);
                    unpaidMap.put(TENE_BILL_HOME_TYPE,homeBill.add(BigDecimal.valueOf(bill.getYsamount())));
                }
            }

        });
        List<HomeBillInfo> resList = new ArrayList<>();
        map.forEach((key, value) -> resList.add(new HomeBillInfo(key,ownerName.get(),value.get(TENE_BILL_ELE_TYPE),value.get(TENE_BILL_HOME_TYPE))));
        resList.sort((kd1, kd2) -> {
            //按照kd1的更新时间进行降序排列
            if (DateUtils.parseDate(kd2.getDate()).before(DateUtils.parseDate(kd1.getDate()))) {
                return 1;
            }
            if (DateUtils.parseDate(kd1.getDate()) == DateUtils.parseDate(kd2.getDate())) {
                return 0;
            }
            return -1;
        });
        List<HomeBillInfo> substrList;
        try {
            substrList = resList.subList((pageNum - 1) * 10, Math.min(pageNum * 10, resList.size()));
        }catch (Exception e){
            throw new CustomException("无更多账单信息");
        }
        return new BillSummaryDataVO(substrList,new HomeBillInfo(ownerName.get(),unpaidMap.get(TENE_BILL_ELE_TYPE),unpaidMap.get(TENE_BILL_HOME_TYPE)));
    }

    @Override
    public HshPageVo<HshMatterHistoryVo> getMatterHis(Long homeId, HshMatterHistoryReqVO reqVO) {

        JSONObject object = HttpUtil.doGetstr(domain + "/estate-frontend/repair/reports?compositeStatus="
                +reqVO.getCompositeStatus()+"&current="+reqVO.getCurrent()+"&size="+reqVO.getSize(), getTenementHeader(homeId));
        HaiShangHaiResVO<HshPageVo<HshMatterHistoryVo>> listResVO = object.toJavaObject(new TypeReference<HaiShangHaiResVO<HshPageVo<HshMatterHistoryVo>>>(){});
        doCheckResponse(listResVO,"查询报事报修历史记录失败");
        List<HshMatterHistoryVo> records = listResVO.getBody().getRecords();
        for (HshMatterHistoryVo record : records) {
            record.setCreateTime(Instant.ofEpochMilli(DateUtils.addHours(new Date(record.getCreateTime().toEpochMilli()),8).getTime()));
        }
        return listResVO.getBody();
    }

    @Override
    public List<SysMatterLog> getMatterLog(SysMatterLog matterLog) {
        return sysTenementMapper.selectMatterLog(matterLog);
    }

    @Override
    public List<SysVisitorDetailLog> getVisitLog(VisitLogReqParamVO paramVO) {
        return sysVisitorLogMapper.selectSysVisitorLogList(paramVO);
    }

    @Override
    public void deleteVisitLog(Long[] ids) {
        sysVisitorLogMapper.deleteSysVisitorLogByIds(ids);
    }

    @Override
    public void deleteMatterLog(Long[] ids) {
        sysTenementMapper.deleteMatterLog(ids);
    }

    @Override
    public List<UploadFile> uploadFile(MultipartFile[] files) throws IOException {
        return uploadFileService.uploadFiles(files);
    }


    /**
     *  校验请求返回的消息类型和状态
     * @param resVO     返回id
     * @param message   返回错误提示
     */
    private void doCheckResponse(HaiShangHaiResVO<?> resVO,String message){

        //  状态码不是200 或 返回的title 不是success
        if (resVO.getStatus() != HttpStatus.SUCCESS || !Constants.LOGIN_SUCCESS.toLowerCase(Locale.ROOT).equals(resVO.getTitle())){
            log.error("物业系统操作失败:{},{}",resVO.getDetail(),resVO.getTitle());
            if ("visitor.name.mismatch".equals(resVO.getTitle())){
                throw new CustomException("访客姓名格式不正确");
            }
            throw new RuntimeException(message);
        }
    }

    /**
     *  校验请求返回的消息类型和状态
     * @param resVO     返回id
     * @param message   返回错误提示
     */
    private void doCheckResponse(HaiShangHaiListResVO<?> resVO,String message){

        //  状态码不是200 或 返回的title 不是success
        if (resVO.getStatus() != HttpStatus.SUCCESS || !Constants.LOGIN_SUCCESS.toLowerCase(Locale.ROOT).equals(resVO.getTitle())){
            log.error("物业系统操作失败:{},{}",resVO.getDetail(),resVO.getTitle());
            throw new RuntimeException(message);
        }
    }

    /**
     *  封装请求的header
     * @param homeId 家庭id
     * @return header
     */
    private  Map<String, String> getTenementHeader(Long homeId){

        String token = service.getCacheObject(CacheConstants.TENEMENT_LOGIN_TOKEN + homeId);
        if (StringUtils.isEmpty(token)){
            this.loginTenement(homeId);
            token = service.getCacheObject(CacheConstants.TENEMENT_LOGIN_TOKEN + homeId);
        }
        Map<String, String> header = new HashMap<>();
        header.put("Content-Type","application/json");
        header.put("X-HainaCloud-Token",token);
        return header;
    }

    private void check(Long deptId){
        //  校验所选部门是否已存在管理的物业
        Long count = sysTenementMapper.selectTeneByDeptId(deptId);
        XunkAssert.isTrue(count == 0,"该社区已存在管理的物业部门");
    }
}
